import requests  # 用于发送网页请求
from bs4 import BeautifulSoup  # 用于解析HTML网页
import re  # 用于正则表达式匹配文本
import openpyxl  # 用于读写Excel文件
import matplotlib.pyplot as plt  # 用于绘制图表
import os  # 用于打开文件或进行文件操作（如打开Excel）

# 设置matplotlib支持中文显示，避免中文乱码
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用“黑体”字体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号'-'显示为方块的问题


def fetch_webpage_text(url: str) -> str:
    """
    获取网页中的纯文本内容
    参数：url - 网页地址
    返回值：纯文本内容
    """
    headers = {"User-Agent": "Mozilla/5.0"}  # 设置请求头，伪装成浏览器
    response = requests.get(url, headers=headers)  # 发送GET请求
    response.encoding = 'utf-8'  # 设置编码为UTF-8，避免乱码
    soup = BeautifulSoup(response.text, 'html.parser')  # 使用BeautifulSoup解析HTML
    return soup.get_text()  # 只提取网页中的文字内容（不包含HTML标签）


def split_city_blocks(text: str):
    """
    将网页内容按省份分段，提取每个省的文字描述部分
    参数：text - 网页中的全部文字
    返回值：一个列表，元素是(省份名称, 对应的文字内容)元组
    """
    # 使用正则查找每个省份的起始位置
    pattern = re.compile(r'2024年1-3月[，,]([\u4e00-\u9fa5]+)总发电量')
    matches = list(pattern.finditer(text))
    blocks = []

    # 遍历所有省份的位置，切分出每个省份的文字内容块
    for i in range(len(matches)):
        start = matches[i].start()  # 本省的起始位置
        city = matches[i].group(1)  # 提取省份名字
        end = matches[i + 1].start() if i + 1 < len(matches) else len(text)  # 终止位置
        block_text = text[start:end]  # 取出该省份段落的全部文字
        blocks.append((city, block_text))  # 添加到列表中
    return blocks


def extract_city_data(city_block: str):
    """
    从一个省份的文字描述中提取发电数据
    参数：city_block - 某一个省份的原始文字段
    返回值：一个列表，包含总发电量、同比、火电、水电、风电、太阳能、核电
    """
    # 定义一个内部函数，用来提取某种发电方式的发电量
    def search_value(pattern_str: str) -> float:
        match = re.search(pattern_str, city_block)
        return float(match.group(1)) if match else 0.0  # 没找到就返回0

    # 首先匹配总发电量和同比增长率
    total_match = re.search(r'总发电量([\d.]+)亿千瓦时，同比增长([-\d.]+)%', city_block)
    if not total_match:
        return None  # 如果没有匹配到总发电量，说明这个省份信息不完整

    total = float(total_match.group(1))  # 提取总发电量
    yoy = total_match.group(2)  # 提取同比增长率（注意是字符串）

    # 分别提取各种发电方式的发电量
    fire = search_value(r'火电发电量([\d.]+)亿千瓦时')
    hydro = search_value(r'水力发电量([\d.]+)亿千瓦时')
    wind = search_value(r'风力发电量([\d.]+)亿千瓦时')
    solar = search_value(r'太阳能发电量([\d.]+)亿千瓦时')
    nuclear = search_value(r'核电发电量([\d.]+)亿千瓦时')

    return [total, yoy, fire, hydro, wind, solar, nuclear]  # 返回数据列表


def parse_all_city_data(blocks):
    """
    对所有省份段落进行数据提取
    参数：blocks - 省份文字段组成的列表
    返回值：字典，键是省份名，值是数据列表
    """
    result = {}
    for city, block in blocks:
        city_data = extract_city_data(block)
        if city_data:
            result[city] = city_data
    return result


def write_to_excel(data: dict, filename: str):
    """
    将最终结果写入Excel表格
    参数：
        data - 包含发电数据的字典
        filename - Excel文件名
    """
    wb = openpyxl.Workbook()  # 创建工作簿
    ws = wb.active  # 获取活动工作表
    ws.title = "发电量数据"  # 设置工作表名字

    # 写入表头
    headers = ["省份", "3月总发电量", "同比增长率", "火电发电量", "水力发电量", "风力发电量", "太阳能发电量", "核电发电量"]
    ws.append(headers)

    # 写入每一行数据
    for province, values in data.items():
        ws.append([province] + values)

    wb.save(filename)  # 保存Excel文件
    os.startfile(filename)  # 自动打开Excel文件（仅Windows有效）


def plot_total_power_bar(data: dict, output_path: str):
    """
    绘制各省总发电量柱状图并保存
    参数：
        data - 发电数据字典
        output_path - 图片保存路径
    """
    provinces = list(data.keys())  # 获取省份列表
    total_powers = [entry[0] for entry in data.values()]  # 提取总发电量

    plt.figure(figsize=(15, 8))  # 设置图像大小
    bars = plt.bar(provinces, total_powers, color='skyblue', edgecolor='black')  # 绘制柱状图

    # 添加每个柱子的数值标签
    for bar in bars:
        height = bar.get_height()
        plt.text(bar.get_x() + bar.get_width() / 2, height + 5, f'{height:.1f}',
                 ha='center', va='bottom', fontsize=10)

    # 添加标题和坐标轴标签
    plt.title('2024年3月各地区总发电量', fontsize=16)
    plt.xlabel('省份', fontsize=12)
    plt.ylabel('发电量（亿千瓦时）', fontsize=12)
    plt.xticks(rotation=45)  # X轴文字旋转45度，防止重叠
    plt.tight_layout()  # 自动调整布局，避免遮挡
    plt.savefig(output_path)  # 保存为PNG图片
    plt.show()  # 显示图表


def main():
    """
    主函数：控制整体流程
    """
    url = "https://www.hxny.com/nd-102461-0-17.html"  # 目标网页
    raw_text = fetch_webpage_text(url)  # 获取网页文本
    city_blocks = split_city_blocks(raw_text)  # 分块提取每个省份的文字段
    city_data = parse_all_city_data(city_blocks)  # 提取出每个省份的数据

    excel_file = "各省3月发电量.xlsx"  # Excel文件名
    image_file = "发电量柱状图.png"  # 柱状图文件名

    write_to_excel(city_data, excel_file)  # 写入Excel
    plot_total_power_bar(city_data, image_file)  # 绘图


# 运行主函数
if __name__ == "__main__":
    main()
